<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 一：
        // function sayHi() {
        //     console.log(name)       // 变量提升 undefined
        //     console.log(age)        // 暂时性死区 报错
        //     var name = 'Lydia'
        //     let age = 21
        // }
        // sayHi()

        // 二：
        // console.log(i)                          // 输出顺序一：第一个for循环中var定义了i，提升至全局 undefined
        // for (var i = 0; i < 3; i++) {
        //     console.log(i);                     // 输出顺序二：每一次循环打印一次i 分别为 0，1，2
        //     setTimeout(() => console.log(i), 1);    // 输出顺序六:setTimeout是一个异步宏任务，他的回调函数会在遍历结束才执行，此时全局中 i=3，所以会输出3次3
        // }
        // console.log(i)                          // 输出顺序三：此时经过3次for循环 全局 i=3 所以输出 3

        // for (let i = 0; i < 3; i++) {
        //     console.log(i);                     // 输出顺序四：此处为 let 声明，在块级作用域中，每一次循环打印一次i 分别为 0，1，2
        //     setTimeout(() => console.log(i), 1);    // 输出顺序七: 在每次的遍历过程中，i 都有一个新值，并且每个值都在循环内的块级作用域中。输出为0，1，2
        // }
        // console.log(i)                          // 输出顺序五: 与输出顺序3相同，打印全局 i,输出为 3

        // 两者区别：
        // 因为let的作用域是块作用域，所以每次JS检测到setTimeOut把setTimeOut放到队列的同时，let定义的i的值也会跟随setTimeOut进去队列。所以每次循环后队列里的setTimeOut里的i的值是不一样的。
        // 而var定义的i是无法进入setTimeOut的。i只能在运行到setTimeOut时才会向外层环境申请i的值，而这个时候i的值已经变成3了。

        // 对第一种for循环问题的解决：因为setTimeOut()是异步执行，所以我们让他立即执行就可以，使用立即执行函数，形成闭包，使得内部函数能够访问外部函数的变量
        // console.log(i);                     // undefined                     
        // for (var i = 0; i < 3; i++) {
        //     console.log(i);                 // 同步，0,1,2
        //     (function (i) {
        //         console.log(i);             // 同步，0,1,2  
        //         setTimeout(function () {
        //             console.log(i);         // 最后执行，0,1,2
        //         }, 1000);
        //     })(i);  
        // }
        // console.log(i)                      // 3

        // 解决方式二：使用setTimeout的第三个参数
        // console.log(i);                        // undefined
        // for (var i = 0; i < 3; i++) {
        //     console.log(i);                    // 0,1,2
        //     setTimeout( function timer(j) {
        //         console.log(j);                // 最后执行 0，1，2
        //     }, 1000, i);
        // }
        // console.log(i);                        // 3

        // 三：
        // const声明常量， 拥有块级作用域   
        // const shape = {
        //     radius: 10,
        //     diameter() {
        //         console.log(this);   // 此处this为shape这个对象
        //         return this.radius * 2;
        //     },
        //     perimeter: () => {
        //         console.log(this);   // 此处箭头函数perimeter在shape中被定义，所以this指向继承父执行上下文，即常量shape,指向window
        //         console.log(this.radius);
        //         return 2 * Math.PI * this.radius; 
        //     }  
        // }

        // console.log(shape.diameter());       // 20

        // console.log(shape.perimeter());      // NaN

        // 四：
        // +true;      // 1
        // !"Lydia";   // false

        // 一元操作符加号尝试将 bool 转为 number。true 转换为 number 的话为 1，false 为 0。
        // 字符串 'Lydia' 是一个真值，真值取反那么就返回 false。

        
        // 五：
        // const bird = {
        //     size: 'small'
        // }

        // const mouse = {
        //     name: 'Mickey',
        //     small: true
        // }
        // // console.log(mouse.bird.size)  // 错误
        // console.log(mouse[bird.size])    // true
        // console.log(mouse[bird["size"]]) // true
        
        //在 JavaScript 中，所有对象的 keys 都是字符串（除非对象是 Symbol）。尽管我们可能不会定义它们为字符串，但它们在底层总会被转换为字符串。
        // 当我们使用括号语法时（[]），JavaScript 会解释（或者 unboxes）语句。它首先看到第一个开始括号 [ 并继续前进直到找到结束括号 ]。只有这样，它才会计算语句的值。
        // mouse[bird.size]：首先计算 bird.size，这会得到 small。mouse["small"] 返回 true。
        // 然后使用点语法的话，上面这一切都不会发生。
        // mouse 没有 bird 这个 key，这也就意味着 mouse.bird 是 undefined。
        // 然后当我们使用点语法 mouse.bird.size 时，因为 mouse.bird 是 undefined，这也就变成了 undefined.size。这个行为是无效的，并且会抛出一个错误类似 Cannot read property "size" of undefined。

        // 六：
        let c = { greeting: 'Hey!' }
        let d

        d = c
        c.greeting = 'Hello'
        console.log(d.greeting)
        // 声明的是一个引用类型的数据，存放于堆内存中
        // 首先，变量 c 的值是一个对象。接下来，我们给 d 分配了一个和 c 对象相同的引用。
        // 因此当我们改变其中一个对象时，其实是改变了所有的对象。我们可以使用浅拷贝改善这个案例
        
        // let c = { greeting: 'Hey!' }
        // let d

        // d = Object.assign({}, c)
        // c.greeting = 'Hello'
        // console.log(d.greeting)
    </script>
</body>
</html>